package com.bigdata.config;/**
 * Created by Administrator on 2019/7/9 0009.
 */

import com.bigdata.security.shiro.filter.LogoutFilter;
import com.bigdata.security.shiro.realm.UserRealm;
import com.bigdata.security.shiro.filter.OAuth2Filter;
import com.bigdata.security.shiro.listener.ShiroSessionListener;
import com.bigdata.security.shiro.matcher.RetryLimitHashedCredentialsMatcher;
import com.bigdata.security.shiro.session.dao.CachingRedisSessionDao;
import com.bigdata.security.shiro.session.dao.ShiroSessionRepository;
import com.bigdata.security.shiro.session.dao.impl.ShiroSessionRepositoryImpl;
import com.bigdata.security.shiro.session.redis.RedisSessionFactory;
import com.bigdata.utils.shiro.ShiroUtils;
import org.apache.commons.io.IOUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionFactory;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.Base64Utils;

import javax.servlet.Filter;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author Administrator
 * @description
 * @date 2019/7/9 0009
 */
@Configuration
public class ShiroConfig {
    public static final String PREMISSION_STRING = "perms[\"{0}\"]";

    // Session超时时间，单位为毫秒（默认30分钟）
    @Value("${shiro.session.expireTime}")
    private int expireTime;

    // 相隔多久检查一次session的有效性，单位毫秒，默认就是10分钟
    @Value("${shiro.session.validationInterval}")
    private int validationInterval;

    // 同一个用户最大会话数
    @Value("${shiro.session.maxSession}")
    private int maxSession;

    // 踢出之前登录的/之后登录的用户，默认踢出之前登录的用户
    @Value("${shiro.session.kickoutAfter}")
    private boolean kickoutAfter;

    // 验证码开关
    @Value("${shiro.user.captchaEnabled}")
    private boolean captchaEnabled;

    // 验证码类型
    @Value("${shiro.user.captchaType}")
    private String captchaType;

    // 设置Cookie的域名
    @Value("${shiro.cookie.domain}")
    private String domain;

    // 设置cookie的有效访问路径
    @Value("${shiro.cookie.path}")
    private String path;

    // 设置HttpOnly属性
    @Value("${shiro.cookie.httpOnly}")
    private boolean httpOnly;

    // 设置Cookie的过期时间，秒为单位
    @Value("${shiro.cookie.maxAge}")
    private int maxAge;

    // 登录地址
    @Value("${shiro.user.loginUrl}")
    private String loginUrl;

    // 权限认证失败地址
    @Value("${shiro.user.unauthorizedUrl}")
    private String unauthorizedUrl;

    /**
     * 自定义Realm
     */
    @Bean
    public UserRealm userRealm(EhCacheManager cacheManager) {
        UserRealm userRealm = new UserRealm();
        userRealm.setCacheManager(cacheManager);
        userRealm.setCredentialsMatcher(createHashedCredentialsMatcher());
        return userRealm;
    }

    public HashedCredentialsMatcher createHashedCredentialsMatcher() {
        RetryLimitHashedCredentialsMatcher matcher = new RetryLimitHashedCredentialsMatcher();
        matcher.setHashAlgorithmName(ShiroUtils.hashAlgorithmName);
        matcher.setCacheManager(getEhCacheManager());
        return matcher;
    }

    /**
     * 缓存管理器 使用Ehcache实现
     */
    @Bean
    public EhCacheManager getEhCacheManager() {
        net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("shiroCache");
        EhCacheManager em = new EhCacheManager();
        if (cacheManager == null) {
            em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream()));
            return em;
        } else {
            em.setCacheManager(cacheManager);
            return em;
        }
    }

    /**
     * 返回配置文件流 避免ehcache配置文件一直被占用，无法完全销毁项目重新部署
     */
    protected InputStream getCacheManagerConfigFileInputStream() {
        String configFile = "classpath:cache/ehcache-shiro.xml";
        InputStream inputStream = null;
        try {
            inputStream = ResourceUtils.getInputStreamForPath(configFile);
            byte[] b = IOUtils.toByteArray(inputStream);
            InputStream in = new ByteArrayInputStream(b);
            return in;
        } catch (IOException e) {
            throw new ConfigurationException(
                    "Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e);
        } finally {
            IOUtils.closeQuietly(inputStream);
        }
    }

    @Bean
    public SessionFactory getSessionFactory() {
        return new RedisSessionFactory();
    }

    @Autowired
    RedisTemplate<Object, Object> redisTemplate;

    @Bean
    public ShiroSessionRepository sessionRepository() {
        ShiroSessionRepositoryImpl repository = new ShiroSessionRepositoryImpl();
        repository.setRedisTemplate(redisTemplate);
        return repository;
    }

    @Bean
    public CachingRedisSessionDao sessionDao() {
        CachingRedisSessionDao sessionDao = new CachingRedisSessionDao();
        sessionDao.setSessionRepository(sessionRepository());
        return sessionDao;
    }

    @Bean("sessionManager")
    public SessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(expireTime * 1000 * 60);
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setSessionFactory(getSessionFactory());
        sessionManager.setSessionDAO(sessionDao());
        sessionManager.setCacheManager(getEhCacheManager());
        sessionManager.setDeleteInvalidSessions(true);
        Collection<SessionListener> listeners = new ArrayList<SessionListener>();
        listeners.add(new ShiroSessionListener());
        sessionManager.setSessionListeners(listeners);
        return sessionManager;
    }

    @Bean
    public org.apache.shiro.mgt.SecurityManager securityManager(UserRealm userRealm, EhCacheManager ehCacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setCacheManager(ehCacheManager);
        securityManager.setSessionManager(sessionManager());
        //securityManager.setRememberMeManager(rememberMeManager()); 不用这个直接cookie
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl(loginUrl);
        shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
        LinkedHashMap<String, String> filterChainDefinitions = new LinkedHashMap<String, String>();
        filterChainDefinitions.put("/static/**", "anon");
        filterChainDefinitions.put("/logout", "logout");
        filterChainDefinitions.put("/mge/adx/**", "authc");//后台建议起名尽量不要/sys 太好猜了容易被找到
        filterChainDefinitions.put("/mge/app/**", "oauth2");//普通用户调用走auth2
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitions);
        Map<String, Filter> filters = new HashMap<String, Filter>(5);
        filters.put("logout", logoutFilter());
        filters.put("oauth2", oAuth2Filter());
        shiroFilterFactoryBean.setFilters(filters);
        return shiroFilterFactoryBean;
    }

    @Bean
    public OAuth2Filter oAuth2Filter() {
        OAuth2Filter oaut = new OAuth2Filter();
        return oaut;
    }

    private LogoutFilter logoutFilter() {
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setLoginUrl(loginUrl);
        logoutFilter.setCacheManager(getEhCacheManager());
        return logoutFilter;
    }

    private SimpleCookie rememberMeCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        cookie.setMaxAge(maxAge * 24 * 60 * 60);
        cookie.setPath(path);
        cookie.setHttpOnly(httpOnly);
        return cookie;
    }

    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        String encryBytes = "fh_key";
        byte[] encryKeyBytes = encryBytes.getBytes(StandardCharsets.UTF_8);
        String rememberKey = Base64Utils.encodeToString(Arrays.copyOf(encryKeyBytes, 16));
        byte[] d = org.apache.shiro.codec.Base64.decode(rememberKey);
        cookieRememberMeManager.setCipherKey(d);
        return cookieRememberMeManager;
    }

//    @Bean
//    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
//        return new LifecycleBeanPostProcessor();
//    }
//
//    @Bean
//    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
//        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
//        advisor.setSecurityManager(securityManager);
//        return advisor;
//    }
}
